#include "AssetReader.h"

#include "Path/Paths.h"
#include "Serialize/BinarySerializer.h"
#include "Async/PackagedTask.h"
#include <fstream>

#include "AssetSubsystem.h"
#include "AssetHead.h"

#include "Texture/Texture2D.h"
#include "AssetObject.h"

std::shared_ptr<CPackagedTask<TVector<uint8_t>>>
    CAssetReader::CreateAsyncReadTask(const SAssetPath &InAssetPath,bool bOnlyAssetHeader)
{
    return CreateAsyncReadTask(
        InAssetPath.GetPackageName()
        ,InAssetPath.GetFolderPathInPackage()
        ,InAssetPath.GetAssetName()
        ,bOnlyAssetHeader
    );
}

std::shared_ptr<CPackagedTask<TVector<uint8_t>>> CAssetReader::CreateAsyncReadTask(const CString &InPackageName, const CString &InAssetPathInPackage, const CString &InAssetName, bool bOnlyAssetHeader)
{
    auto Task=WH::CreateAsyncTask<TVector<uint8_t>>([InPackageName,InAssetPathInPackage,InAssetName,bOnlyAssetHeader](bool& bSucceed) -> TVector<uint8_t>
    {
        //1.find Package folder
        SPath PackageFolder= RAssetSubsystem::Get()->FindPackage(InPackageName).Path;

        if(!PackageFolder.Exists())
        {
            //package folder not exist
            bSucceed=false;
            return TVector<uint8_t>();
        }

        //2.find asset file
        SPath AssetFile = PackageFolder 
            / InAssetPathInPackage 
            /(InAssetName +U".asset");
        if(!AssetFile.Exists())
        {
            //asset file not exist
            bSucceed=false;
            return TVector<uint8_t>();
        }

        //3.read asset file into memory
        TVector<uint8_t> FileContent;
        {
            std::ifstream File(AssetFile.GetLowLevel(), std::ios::ate | std::ios::binary);
            if(!File.is_open())
            {
                //open file failed
                bSucceed=false;
                return TVector<uint8_t>();
            }

            if(bOnlyAssetHeader)
            {
                //at the begining of the file, there is a uint64_t to store the asset header size
                uint64_t AssetHeaderSize = 0;  
                {
                    TVector<uint8_t> FileHeadSizeData;
                    FileHeadSizeData.Resize(sizeof(uint64_t));
                    File.seekg(0);
                    File.read((char*)FileHeadSizeData.Data(), sizeof(uint64_t));
                    File.seekg(0);

                    CBinaryDeserializer Serializer(FileHeadSizeData);
                    Serializer >> AssetHeaderSize;
                }

                //the loaded data is the asset header
                FileContent.Resize(AssetHeaderSize);
                File.read((char*)FileContent.Data(), AssetHeaderSize);
            }
            else
            {
                auto FileSize = File.tellg();
                FileContent.Resize(FileSize/sizeof(uint8_t));
                File.seekg(0);
                File.read((char*)FileContent.Data(), FileSize);
            }
        }

        return FileContent;
    });

    return Task;
}

std::shared_ptr<CPackagedTask<TVector<uint8_t>>> CAssetReader::CreateAsyncReadTask(const SPath &InAssetFilePath, bool bOnlyAssetHeader)
{
    auto Task=WH::CreateAsyncTask<TVector<uint8_t>>([InAssetFilePath,bOnlyAssetHeader](bool& bSucceed) -> TVector<uint8_t>
    {
        //1.read asset file into memory
        TVector<uint8_t> FileContent;
        {
            std::ifstream File(InAssetFilePath.GetLowLevel(), std::ios::ate | std::ios::binary);
            if(!File.is_open())
            {
                //open file failed
                bSucceed=false;
                return TVector<uint8_t>();
            }

            //先检查文件大小
            if(File.tellg()<=0)
            {
                //文件为空
                bSucceed=false;
                return TVector<uint8_t>();
            }

            if(bOnlyAssetHeader)
            {
                //at the begining of the file, there is a uint64_t to store the asset header size
                uint64_t AssetHeaderSize = 0;  
                {
                    TVector<uint8_t> FileHeadSizeData;
                    FileHeadSizeData.Resize(sizeof(uint64_t));
                    File.seekg(0);
                    File.read((char*)FileHeadSizeData.Data(), sizeof(uint64_t));
                    File.seekg(0);

                    CBinaryDeserializer Serializer(FileHeadSizeData);
                    Serializer >> AssetHeaderSize;
                }

                //the loaded data is the asset header
                FileContent.Resize(AssetHeaderSize);
                File.read((char*)FileContent.Data(), AssetHeaderSize);
            }
            else
            {
                auto FileSize = File.tellg();
                FileContent.Resize(FileSize/sizeof(uint8_t));
                File.seekg(0);
                File.read((char*)FileContent.Data(), FileSize);
            }
        }

        return FileContent;
    });

    return Task;
}

// std::shared_ptr<CPackagedTask<TVector<SAssetPackage>>> CAssetReader::CreateAsyncFindAllAssetPackagesTask(const SPath& Folder)
// {
//     auto Task=WH::CreateAsyncTask<TVector<SAssetPackage>>([this,Folder](bool&) -> TVector<SAssetPackage>
//     {
//         TVector<SAssetPackage> Result;

//         if(Folder.Exists())
//         {
//             std::filesystem::directory_iterator It(Folder.GetLowLevel());
//             std::filesystem::directory_iterator EndIt;
        
//             for(; It != EndIt; ++It)
//             {
//                 //check if it is a directory
//                 if (It->is_directory())
//                 {
//                     //save it to asset subsystem
//                     //use folder name as package name
//                     SAssetPackage Package;
//                     Package.Name=SPath(It->path().filename()).ToString();
//                     Package.Path=SPath(It->path()).ToString();
//                     Result.Add(Package);
//                 }
//             }
//         }
        
    
//         return Result;
//     });

//     return Task;
// }

// std::shared_ptr<CPackagedTask<SAssetPath>> CAssetReader::CreateAsyncRenameAssetTask(
//     const SAssetPath& InAsset
//     ,const CString& InNewPackageName
//     ,const CString& InAssetPathInPackage
//     ,const CString& InNewAssetName
//     )
// {
//     auto Task=WH::CreateAsyncTask<SAssetPath>([InAsset,InNewPackageName,InAssetPathInPackage,InNewAssetName](bool& bSucceed) -> SAssetPath
//     {
//         //1. replace the asset head , and save it
//         SAssetPath OldAssetPath=InAsset;
//         SAssetPath NewAssetPath=SAssetPath(
//             InAsset.GetAssetType()
//             ,InNewPackageName
//             ,InAssetPathInPackage
//             ,InNewAssetName);

//         CString OldAssetObjPath= RAssetObject::CreatePath(InAsset.GetAssetType(),InAsset.GetPackageName(),InAsset.GetFolderPathInPackage(),InAsset.GetAssetName());
//         CString NewAssetObjPath= RAssetObject::CreatePath(InAsset.GetAssetType(),InAsset.GetPackageName(),InAsset.GetFolderPathInPackage(),InNewAssetName);
//         {
//             SPath PackageFolder= RAssetSubsystem::Get()->FindPackage(OldAssetPath.GetPackageName()).Path;

//             if(!PackageFolder.Exists())
//             {
//                 assert(false&& "package folder not exist");
//             }

//             //2.find asset file
//             SPath AssetFile = PackageFolder 
//                 / OldAssetPath.GetFolderPathInPackage()
//                 /(OldAssetPath.GetAssetName() +U".asset");

//             if(!AssetFile.Exists())
//             {
//                 assert(false&& "asset file not exist");
//             }

//             std::ifstream File(AssetFile.GetLowLevel(),  std::ios::binary);
//             if(!File.is_open())
//             {
//                 assert(false &&"Load file failed");
//                 return SAssetPath();
//             }

//             //read whole file
//             File.seekg(0, std::ios::end);
//             TVector<uint8_t> AssetFileData;
//             AssetFileData.Resize(File.tellg());

//             File.seekg(0);
//             File.read((char*)AssetFileData.Data(),AssetFileData.Num());
//             File.close();

//             SAssetHead OldHead= RAssetSubsystem::Get()->AnyThreadParseAssetHead(AssetFileData);

//             //3. rename asset file head
//             SAssetHead NewHead=OldHead;
//             NewHead.AssetName=InNewAssetName;

//             //replace asset name in head
//             for(auto & ObjectPath:NewHead.InternalObjects)
//             {
//                 if(ObjectPath.StartsWith(OldAssetObjPath))
//                 {
//                     ObjectPath=ObjectPath.Replace(OldAssetObjPath,NewAssetObjPath);
//                 }
//             }

//             RAssetSubsystem::ReplaceHead(AssetFileData,OldHead,NewHead);
            
//             //4. save asset
//             SPath NewPackageFolder= RAssetSubsystem::Get()->FindPackage(InNewPackageName).Path;
//             if(!NewPackageFolder.Exists())
//             {
//                 assert(false&& "package folder not exist");
//             }

//             SPath NewAssetFile = NewPackageFolder 
//                 / NewAssetPath.GetFolderPathInPackage()
//                 /(NewAssetPath.GetAssetName() +U".asset");

//             //create folders if not exist
//             if(!NewAssetFile.Parent().Exists())
//             {
//                 std::filesystem::create_directories(NewAssetFile.Parent().GetLowLevel());
//             }


//             //3 just  write to the file
//             std::ofstream FileForWrite(NewAssetFile.GetLowLevel(), std::ios::binary);
//             if(!FileForWrite.is_open())
//             {
//                 //open file failed
//                 assert(false &&"Load file failed");
//                 return SAssetPath();
//             }

//             FileForWrite.write((char*)AssetFileData.Data(), AssetFileData.Num());

//             //5. delete old asset file
//             AssetFile.Delete();
//         }

//         //2.find all asset file which reference the asset
       
//         TVector<SPath> AllAssetFiles;
//         for(auto& Package: RAssetSubsystem::Get()->GetAllPackages())
//         {
//             SPath PackageFolder= SPath(Package.Path);

//             if(!PackageFolder.Exists())
//             {
//                 assert(false&& "package folder not exist");
//             }

//             //recursively search all asset file in the package folder
//             TVector<SPath> AllFoldersToSearch;
//             AllFoldersToSearch.Add(PackageFolder);

//             while(!AllFoldersToSearch.Empty())
//             {
//                 SPath FolderToSearch=AllFoldersToSearch.Last();
//                 AllFoldersToSearch.RemoveLast();

//                 std::filesystem::directory_iterator It(FolderToSearch.GetLowLevel());
//                 std::filesystem::directory_iterator EndIt;

//                 for(; It != EndIt; ++It)
//                 {
//                     //check if it is a directory
//                     if (It->is_directory())
//                     {
//                         AllFoldersToSearch.Add(SPath(It->path()));
//                     }
//                     else
//                     {
//                         //check if it is a asset file, asset file have extension ".asset"
//                         if(It->path().extension() != ".asset")
//                         {
//                             continue;
//                         }

//                         AllAssetFiles.Add(SPath(It->path()));
//                     }
//                 }

//             }
        
//         }

//         //3. rename asset reference in other asset file
//         for(auto &AssetFile:AllAssetFiles)
//         {
//             std::ifstream File(AssetFile.GetLowLevel(),  std::ios::binary);
//             if(!File.is_open())
//             {
//                 assert(false &&"Load file failed");
//                 return SAssetPath();
//             }

//             //at the begining of the file, there is a uint64_t to store the asset header size
//             uint64_t AssetHeaderSize = 0;  
//             {
//                 TVector<uint8_t> FileHeadSizeData;
//                 FileHeadSizeData.Resize(sizeof(uint64_t));
//                 File.read((char*)FileHeadSizeData.Data(), sizeof(uint64_t));
//                 File.seekg(0);

//                 CBinaryDeserializer Serializer(FileHeadSizeData);
//                 Serializer >> AssetHeaderSize;
//                 assert(AssetHeaderSize>0 && "Asset header size cant be 0");
//             }

//             //read the asset header
//             TVector<uint8_t> AssetHeaderData;
//             AssetHeaderData.Resize(AssetHeaderSize);
//             File.read((char*)AssetHeaderData.Data(), AssetHeaderSize);

//             SAssetHead Head= RAssetSubsystem::Get()->AnyThreadParseAssetHead(AssetHeaderData);
        
//             //check if the asset file depend on the old asset
//             auto Found=Head.Dependencies.Find(OldAssetPath);
//             if(!Found.HasValue())
//             {
//                 continue;
//             }

//             SAssetHead NewHead=Head;
//             NewHead.Dependencies[Found.GetValue()] = NewAssetPath;

//             for(auto& External:NewHead.ExternalObjects)
//             {
//                 if(External.StartsWith(OldAssetObjPath))
//                 {
//                     External=External.Replace(OldAssetObjPath,NewAssetObjPath);
//                 }
//             }

//             //read whole file
//             File.seekg(0, std::ios::end);
//             TVector<uint8_t> FileData;
//             FileData.Resize(File.tellg());

//             File.seekg(0);
//             File.read((char*)FileData.Data(),FileData.Num());
//             File.close();

//             RAssetSubsystem::Get()->ReplaceHead(FileData,Head,NewHead);

//             //save file
//             std::ofstream OutFile(AssetFile.GetLowLevel(),  std::ios::binary);
//             if(!OutFile.is_open())
//             {
//                 assert(false &&"Load file failed");
//                 return SAssetPath();
//             }

//             OutFile.write((char*)FileData.Data(),FileData.Num());
//             OutFile.close();
//         }

//         return NewAssetPath;

//     });

//     return Task;
// }

void CAssetReader::AsyncFindItemsInFolderTask(
    const CString &InPackageName
    , const TVector<CString> &InQueryFolderRelativePath
    , const TDelegate<void, CString> &OnFoundDirectoy
    , const TDelegate<void, const SAssetPath &/*Asset Path*/,const SAssetHead &,TSharedObjectPtr<RTexture2D>> &OnFoundAssetFile
    , const TDelegate<bool> CanContinue
    , const TDelegate<void> &OnFinished)
{
    auto Task=WH::CreateAsyncTask<void>(
        [this,InPackageName,InQueryFolderRelativePath,OnFoundDirectoy,OnFoundAssetFile,OnFinished
        ,CanContinue](bool&)
    {
        //1.find Package folder
        SPath PackageFolder= RAssetSubsystem::Get()->FindPackage(InPackageName).Path;

        if(!PackageFolder.Exists())
        {
            assert(false&& "package folder not exist");
        }

        //2.find folder to search
        SPath AssetFolder = PackageFolder;
        
        CString FolderRelativePath=U"./";

        for(auto &Folder:InQueryFolderRelativePath)
        {
            FolderRelativePath=FolderRelativePath+ U"/"+ Folder;
        }  

        AssetFolder /= FolderRelativePath;

        if(!AssetFolder.Exists())
        {
            assert(false&& "asset file not exist");
        }

        //iterater all directory and asset file
        std::filesystem::directory_iterator It(AssetFolder.GetLowLevel());
        std::filesystem::directory_iterator EndIt;

        TVector<CString> AllSubDirectories;
        TVector<SAssetPath> AllAssetPaths;

        for(; It != EndIt; ++It)
        {
            if(CanContinue.IsBound() &&! CanContinue.Execute())
            {
                return;
            }

            //check if it is a directory
            if (It->is_directory())
            {
                //create a main thread task to execute the delegate
                CString DirectoryName = SPath(It->path().filename()).ToString();
                AllSubDirectories.Add(DirectoryName);
                // auto NotifyTask=WH::CreateMainThreadTask<void>([OnFoundDirectoy,DirectoryName,CanContinue](bool&)
                // {
                //     if(CanContinue.IsBound() &&! CanContinue.Execute())
                //     {
                //         return;
                //     }

                //     //use folder name as package name
                //     OnFoundDirectoy.ExecuteIfBound(DirectoryName);
                // });

                // //execute the task in main thread
                // NotifyTask->Start();
            }
            else
            {
                //check if it is a asset file, asset file have extension ".asset"
                if(It->path().extension() != ".asset")
                {
                    continue;
                }

                //read the file header to get the asset path
                SPath AssetFile = SPath(It->path());
                std::ifstream File(AssetFile.GetLowLevel(),  std::ios::binary);
                if(!File.is_open())
                {
                    assert(false &&"Load file failed");
                    return;
                }


                //at the begining of the file, there is a uint64_t to store the asset header size
                uint64_t AssetHeaderSize = 0;  
                {
                    TVector<uint8_t> FileHeadSizeData;
                    FileHeadSizeData.Resize(sizeof(uint64_t));
                    File.read((char*)FileHeadSizeData.Data(), sizeof(uint64_t));
                    File.seekg(0);

                    CBinaryDeserializer Serializer(FileHeadSizeData);
                    Serializer >> AssetHeaderSize;
                    assert(AssetHeaderSize>0 && "Asset header size cant be 0");
                }

                //read the asset header
                TVector<uint8_t> AssetHeaderData;
                AssetHeaderData.Resize(AssetHeaderSize);
                File.read((char*)AssetHeaderData.Data(), AssetHeaderSize);

                SAssetHead Head= RAssetSubsystem::Get()->AnyThreadParseAssetHead(AssetHeaderData);
                SAssetPath AssetPath=SAssetPath(
                    Head.MainObjectClass
                    ,InPackageName
                    ,FolderRelativePath
                    ,Head.AssetName
                );

                AllAssetPaths.Add(AssetPath);

                // RAssetSubsystem::Get()->AsyncLoadAsset(AssetPath, EAssetSection::Thumbnail, 
                // TDelegate<void ,SAssetLoadResult> ::Create(
                //     [OnFoundAssetFile,AssetPath](const SAssetLoadResult& LoadResult)->void{
                            
                //             OnFoundAssetFile.ExecuteIfBound(AssetPath,LoadResult.Head,LoadResult.Thumbnail);
                //     }
                // ));
            }
        }


        //notify loaded directories
        for(auto &Directory:AllSubDirectories)
        {
            auto NotifyTask=WH::CreateMainThreadTask<void>([OnFoundDirectoy,Directory,CanContinue](bool&)
            {
                if(CanContinue.IsBound() &&! CanContinue.Execute())
                {
                    return;
                }

                //use folder name as package name
                OnFoundDirectoy.ExecuteIfBound(Directory);
            });
            NotifyTask->Start();
        }

        auto MainThreadLoadAssetFun=[OnFoundAssetFile,CanContinue,OnFinished](
                const SAssetPath & AssetPath  
                ,std::shared_ptr<CPackagedTask<void>> NextTask
            ){
            if(CanContinue.IsBound() &&! CanContinue.Execute())
            {
                OnFinished.ExecuteIfBound();
                return;
            }
            
            RAssetSubsystem::Get()->AsyncLoadAsset(AssetPath, EAssetSection::Thumbnail, 
                TDelegate<void ,SAssetLoadResult> ::Create(
                    [OnFoundAssetFile,AssetPath,CanContinue,OnFinished,NextTask](const SAssetLoadResult& LoadResult)->void{
                        if(CanContinue.IsBound() &&! CanContinue.Execute())
                        {
                            OnFinished.ExecuteIfBound();
                            return;
                        }
                        
                        OnFoundAssetFile.ExecuteIfBound(AssetPath,LoadResult.Head,LoadResult.Thumbnail);

                        if(NextTask)
                        {
                            NextTask->Start();
                        }
                    }
            ));
        };


        //notify  finished if no asset
        if(AllAssetPaths.Num()==0)
        {
            //because main thread task will invoked in start order
            //so do not need ThenInMainThread after directly notify
            auto Task=WH::CreateMainThreadTask<void>([OnFinished](bool&) ->void
            {
                OnFinished.ExecuteIfBound();
            });
            Task->Start();
        }
        else
        {
            auto LastAsset=AllAssetPaths.Last();
            auto LastTask=WH::CreateMainThreadTask<void>([LastAsset,CanContinue,OnFinished,MainThreadLoadAssetFun](bool& Continue)
                {
                    if(CanContinue.IsBound() &&! CanContinue.Execute())
                    {
                        Continue=false;
                        OnFinished.ExecuteIfBound();
                        return;
                    }

                    MainThreadLoadAssetFun(LastAsset,nullptr);
                });

            for(int32_t AssetPathIndex=AllAssetPaths.Num()-2;
                AssetPathIndex>=0; 
                AssetPathIndex--)
            {
                auto AssetPath=AllAssetPaths[AssetPathIndex];
                auto Task=WH::CreateMainThreadTask<void>([
                    CanContinue,
                    OnFinished,
                    MainThreadLoadAssetFun,
                    AssetPath,
                    LastTask
                    ](bool& Continue)
                {
                    if(CanContinue.IsBound() &&! CanContinue.Execute())
                    {
                        Continue=false;
                        OnFinished.ExecuteIfBound();
                        return;
                    }
                    MainThreadLoadAssetFun(AssetPath,LastTask);
                });

                LastTask=Task;
            }

            LastTask->Start();
        }


    });

    Task->Start();
}

void CAssetReader::AsyncFindItemsMatchClass(CClass *InClass, const TVector<SAssetPackage> &InPackages, const TDelegate<void, const SAssetPath &, const SAssetHead &,TSharedObjectPtr<RTexture2D> > &OnFoundAssetFile, const TDelegate<bool> CanContinue, const TDelegate<void> &OnFinished)
{
    auto Task=WH::CreateAsyncTask<void>(
        [this,InPackages,OnFoundAssetFile,OnFinished
        ,CanContinue,InClass](bool&)
    {

        for(auto &Package:InPackages)
        {

            SPath PackageFolder= SPath(Package.Path);

            if(!PackageFolder.Exists())
            {
                assert(false&& "package folder not exist");
            }

            TVector<SPath> AllFoldersToSearch;
            AllFoldersToSearch.Add(PackageFolder);

            //search
            while(!AllFoldersToSearch.Empty())
            {
                if(CanContinue.IsBound() &&! CanContinue.Execute())
                {
                    return;
                }

                SPath FolderToSearch=AllFoldersToSearch.Last();
                AllFoldersToSearch.RemoveLast();

                std::filesystem::directory_iterator It(FolderToSearch.GetLowLevel());
                std::filesystem::directory_iterator EndIt;

                for(; It != EndIt; ++It)
                {
                    if(CanContinue.IsBound() &&! CanContinue.Execute())
                    {
                        return;
                    }

                    //check if it is a directory
                    if (It->is_directory())
                    {
                        AllFoldersToSearch.Add(SPath(It->path()));
                    }
                    else
                    {
                        //check if it is a asset file, asset file have extension ".asset"
                        if(It->path().extension() != ".asset")
                        {
                            continue;
                        }

                        //read the file header to get the asset path
                        SPath AssetFile = SPath(It->path());

                        std::ifstream File(AssetFile.GetLowLevel(),  std::ios::binary);
                        if(!File.is_open())
                        {
                            assert(false &&"Load file failed");
                            return;
                        }

                        //at the begining of the file, there is a uint64_t to store the asset header size
                        uint64_t AssetHeaderSize = 0;  
                        {
                            TVector<uint8_t> FileHeadSizeData;
                            FileHeadSizeData.Resize(sizeof(uint64_t));
                            File.read((char*)FileHeadSizeData.Data(), sizeof(uint64_t));
                            File.seekg(0);

                            CBinaryDeserializer Serializer(FileHeadSizeData);
                            Serializer >> AssetHeaderSize;
                            assert(AssetHeaderSize>0 && "Asset header size cant be 0");
                        }

                        //read the asset header
                        TVector<uint8_t> AssetHeaderData;
                        AssetHeaderData.Resize(AssetHeaderSize);
                        File.read((char*)AssetHeaderData.Data(), AssetHeaderSize);

                        SAssetHead Head= RAssetSubsystem::Get()->AnyThreadParseAssetHead(AssetHeaderData);
                        
                        if(!Head.MainObjectClass->IsA(InClass))
                        {
                            continue;
                        }

                        SAssetPath AssetPath=SAssetPath(
                            Head.MainObjectClass
                            ,Package.Name
                            ,SAssetPath::AbstractFolderPathInPackage(
                                PackageFolder
                                ,AssetFile
                                )
                            ,Head.AssetName
                        );

                        RAssetSubsystem::Get()->AsyncLoadAsset(AssetPath, EAssetSection::Thumbnail, 
                            TDelegate<void ,SAssetLoadResult> ::Create(
                            [OnFoundAssetFile,AssetPath](const SAssetLoadResult& LoadResult)->void{
                                    OnFoundAssetFile.ExecuteIfBound(AssetPath,LoadResult.Head,LoadResult.Thumbnail);
                            }
                        ));
                    }

                }
            }
        }
    });

    Task->ThenInMainThread<void>([OnFinished](bool&) ->void
    {
        OnFinished.ExecuteIfBound();
    });

    Task->Start();    

}

void CAssetReader::AsyncFindFirstItemMatchClass(
    CClass *InClass
    , const TVector<SAssetPackage> &InPackages
    , const TDelegate<void, const SAssetPath &, const SAssetHead &, TSharedObjectPtr<RTexture2D>> &OnFoundAssetFile
    , const TDelegate<void> &OnFindNothing)
{
    auto Task=WH::CreateAsyncTask<void>(
        [this,InPackages,OnFoundAssetFile,OnFindNothing
        ,InClass](bool&) ->void  
    {
        for(auto &Package:InPackages)
        {

            SPath PackageFolder= SPath(Package.Path);

            if(!PackageFolder.Exists())
            {
                assert(false&& "package folder not exist");
            }

            TVector<SPath> AllFoldersToSearch;
            AllFoldersToSearch.Add(PackageFolder);

            //search
            while(!AllFoldersToSearch.Empty())
            {
                SPath FolderToSearch=AllFoldersToSearch.Last();
                AllFoldersToSearch.RemoveLast();

                std::filesystem::directory_iterator It(FolderToSearch.GetLowLevel());
                std::filesystem::directory_iterator EndIt;

                for(; It != EndIt; ++It)
                {
                    //check if it is a directory
                    if (It->is_directory())
                    {
                        AllFoldersToSearch.Add(SPath(It->path()));
                    }
                    else
                    {
                        //check if it is a asset file, asset file have extension ".asset"
                        if(It->path().extension() != ".asset")
                        {
                            continue;
                        }

                        //read the file header to get the asset path
                        SPath AssetFile = SPath(It->path());

                        std::ifstream File(AssetFile.GetLowLevel(),  std::ios::binary);
                        if(!File.is_open())
                        {
                            assert(false &&"Load file failed");
                            return;
                        }

                        //at the begining of the file, there is a uint64_t to store the asset header size
                        uint64_t AssetHeaderSize = 0;  
                        {
                            TVector<uint8_t> FileHeadSizeData;
                            FileHeadSizeData.Resize(sizeof(uint64_t));
                            File.read((char*)FileHeadSizeData.Data(), sizeof(uint64_t));
                            File.seekg(0);

                            CBinaryDeserializer Serializer(FileHeadSizeData);
                            Serializer >> AssetHeaderSize;
                            assert(AssetHeaderSize>0 && "Asset header size cant be 0");
                        }

                        //read the asset header
                        TVector<uint8_t> AssetHeaderData;
                        AssetHeaderData.Resize(AssetHeaderSize);
                        File.read((char*)AssetHeaderData.Data(), AssetHeaderSize);

                        SAssetHead Head= RAssetSubsystem::Get()->AnyThreadParseAssetHead(AssetHeaderData);
                        
                        if(!Head.MainObjectClass->IsA(InClass))
                        {
                            continue;
                        }

                        SAssetPath AssetPath=SAssetPath(
                            Head.MainObjectClass
                            ,Package.Name
                            ,SAssetPath::AbstractFolderPathInPackage(
                                PackageFolder
                                ,AssetFile
                                )
                            ,Head.AssetName
                        );

                        RAssetSubsystem::Get()->AsyncLoadAsset(AssetPath, EAssetSection::Thumbnail, 
                            TDelegate<void ,SAssetLoadResult> ::Create(
                            [OnFoundAssetFile,AssetPath](const SAssetLoadResult& LoadResult)->void{
                                    OnFoundAssetFile.ExecuteIfBound(AssetPath,LoadResult.Head,LoadResult.Thumbnail);
                            }
                        ));

                        return;

                    }
                }
            }

        }

        //nothing found
        auto NotifyTask=WH::CreateMainThreadTask<void>([OnFindNothing](bool&)
        {
            OnFindNothing.ExecuteIfBound();
        });
        NotifyTask->Start();

        return;
    });

    Task->Start();

}

void CAssetReader::AsyncFindAllAssetInPackaged(
    const SAssetPackage &InPackage
    , const TDelegate<void
        , const  TVector<SAssetPathAndHead>  &
        > &OnFoundAssetFile
    , const TDelegate<void> & OnFinished 
    , std::chrono::file_clock::time_point InLastWriteTime        
)
{
    auto Task=WH::CreateAsyncTask<void>(
        [this,InPackage,OnFoundAssetFile,InLastWriteTime](bool&)
    {
        //1.find Package folder
        SPath PackageFolder= InPackage.Path;

        if(!PackageFolder.Exists())
        {
            assert(false&& "package folder not exist");
        }

        //2.find folder to search
        SPath AssetFolder = PackageFolder;
        
        if(!AssetFolder.Exists())
        {
            assert(false&& "asset file not exist");
        }

        //iterater all directory and asset file
        std::filesystem::directory_iterator It(AssetFolder.GetLowLevel());
        std::filesystem::directory_iterator EndIt;

        TVector<CString> AllSubDirectories;
        //TVector<SAssetPath> AllAssetPaths;
        TVector<SAssetPathAndHead> Result;
        for(; It != EndIt; ++It)
        {
            //check if it is a directory
            if (It->is_directory())
            {
                //create a main thread task to execute the delegate
                CString DirectoryName = SPath(It->path().filename()).ToString();
                AllSubDirectories.Add(DirectoryName);
            }
            else
            {
                //check if it is a asset file, asset file have extension ".asset"
                if(It->path().extension() != ".asset")
                {
                    continue;
                }

                //check if the file is newer than the last write time
                if(std::filesystem::last_write_time(It->path())<InLastWriteTime)
                {
                    continue;
                }

                //read the file header to get the asset path
                SPath AssetFile = SPath(It->path());
                std::ifstream File(AssetFile.GetLowLevel(),  std::ios::binary);
                if(!File.is_open())
                {
                    //load file failed
                    assert(false&& "Load file failed");
                    continue;
                }

                //check if the file is empty
                if(File.peek() == std::ifstream::traits_type::eof())
                {
                    continue;
                }


                //at the begining of the file, there is a uint64_t to store the asset header size
                uint64_t AssetHeaderSize = 0;  
                {
                    TVector<uint8_t> FileHeadSizeData;
                    FileHeadSizeData.Resize(sizeof(uint64_t));
                    File.read((char*)FileHeadSizeData.Data(), sizeof(uint64_t));
                    File.seekg(0);

                    CBinaryDeserializer Serializer(FileHeadSizeData);
                    Serializer >> AssetHeaderSize;
                    assert(AssetHeaderSize>0 && "Asset header size cant be 0");
                }

                //read the asset header
                TVector<uint8_t> AssetHeaderData;
                AssetHeaderData.Resize(AssetHeaderSize);
                File.read((char*)AssetHeaderData.Data(), AssetHeaderSize);

                auto& NewItem=Result.Emplace();
                NewItem.AssetHead= RAssetSubsystem::Get()->AnyThreadParseAssetHead(AssetHeaderData);
                NewItem.AssetPath=SAssetPath(
                    NewItem.AssetHead.MainObjectClass
                    ,InPackage.Name
                    ,SAssetPath::AbstractFolderPathInPackage(
                        PackageFolder
                        ,AssetFile
                        )
                    ,NewItem.AssetHead.AssetName
                );

                NewItem.AssetFilePath=AssetFile;

                //AllAssetPaths.Add(AssetPath);


            }
        }

        //notify finished
        auto NotifyTask=WH::CreateMainThreadTask<void>([OnFoundAssetFile,Result](bool&)
        {
            OnFoundAssetFile.ExecuteIfBound(Result);
        });
        NotifyTask->Start();

        // for(auto& AssetPath:AllAssetPaths)
        // {
        //     RAssetSubsystem::Get()->AsyncLoadAsset(AssetPath, EAssetSection::Thumbnail, 
        //         TDelegate<void ,SAssetLoadResult> ::Create(
        //             [OnFoundAssetFile,AssetPath](const SAssetLoadResult& LoadResult)->void{
        //                 OnFoundAssetFile.ExecuteIfBound(AssetPath,LoadResult.Head,LoadResult.Thumbnail);
        //             }
        //         )
        //     );
        // }
    });

    Task->Start();
}
